#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <iostream>
#include <stdlib.h>
#include <cstdlib>
#include <math.h>
#include <vector>

int trans = 0;		//which transformation
bool mesh = true;

#include "drawing.cpp"
//#include "camera.h"
//#include "color.h" //in drawing.cpp

using namespace std;


#define PI 3.1415926535897932384626433832795028841971

//Global Variables
bool axes = false;	//print axes? (toggle with keypress)


PerspectiveCamera* camera;

//interactive mouse controls
int mouseButton;
int mouseX = 0;
int mouseY = 0;
bool controlPressed;




void mouse(int button, int state, int x, int y) {
  // Save the current state of the mouse.  This will be
  // used by the 'motion' function
  mouseButton = button;
  mouseX = x;
  mouseY = y;
  controlPressed = glutGetModifiers() & GLUT_ACTIVE_CTRL;
}

void motion(int x, int y) {

	// Left button = rotation
	// (rotate camera around the up and horizontal vectors)
 	if (mouseButton == GLUT_LEFT_BUTTON) {
		camera->rotateCamera(0.005*(mouseX-x), 0.005*(mouseY-y));
	}
	// Middle button = translation
	// (move camera perpendicular to the direction vector)
	else if (mouseButton == GLUT_MIDDLE_BUTTON) {
		camera->truckCamera((mouseX-x)*0.5, (y-mouseY)*0.5);
	}
	// Right button = dolly or zoom
	// (move camera along the direction vector)
	else if (mouseButton == GLUT_RIGHT_BUTTON) {
	if (controlPressed) {
		camera->zoomCamera(mouseY-y);
	} else {
		camera->dollyCamera(mouseY-y);
	}

	mouseX = x;
	mouseY = y;

	// Redraw the scene with the new camera parameters
	}
	glutPostRedisplay();
}

int HandleGLError() {
  	GLenum error;
  	int i = 0;
  	while ((error = glGetError()) != GL_NO_ERROR) {
  		printf ("GL ERROR(%d):  %s\n", i, gluErrorString(error));
  		i++;
  	}
  	if (i == 0) return 1;
  	return 0;
}


inline Vec3f ComputeNormal(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) {
	//Computes a normal vector from 3 points in a plane
	Vec3f v12 = p2;
	v12 -= p1;
  	Vec3f v23 = p3;
  	v23 -= p2;
  	Vec3f normal;
  	Vec3f::Cross3(normal,v12,v23);
  	normal.Normalize();
  	return normal;
}

void InsertNormal(const Vec3f &p1, const Vec3f &p2, const Vec3f &p3) {
	Vec3f normal = ComputeNormal(p1,p2,p3);
	glNormal3f(normal.x(),normal.y(),normal.z());
}


void handleKeypress(unsigned char key, int x, int y) {
	/*Takes key input and acts accordingly
	*/
	switch (key) {
		case 27: //Escape key
			exit(0);
			break;
		case 'q':
			exit(0);
			break;		
		case 'a': 
			axes = !axes;
			break;	
		case '0': //Escape key
			trans = 0;
			break;
		case '1':
			trans = 1;
			break;		
		case '2': 
			trans = 2;
			break;
		case '3':
			trans = 3;
			break;		
		case '4': 
			trans = 4;
			break;
		case 'm':
			mesh = !mesh;
			break;
	}
	glutPostRedisplay();
}

void initRendering() {
	/*This function handles initializations and places the camera to start facing
		the original plane drawn
	*/
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);

	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	
	//beginning camera position
	const Vec3f camera_position = Vec3f(8,-60,4);
	const Vec3f direction = Vec3f(8.0f,0.0f,4.0f);
	const Vec3f up = Vec3f(0,0,1);
	float scale = 3;
	camera = new PerspectiveCamera(camera_position, direction, up, scale);
	
	initTea();

}

void handleResize(int w, int h) {
	/*This handles when the window is resized.
	*/
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (float)w / (float)h, 10.0, 100.0);
	glTranslatef(0.0f, 0.0f, -20.0f);
}

void drawScene() {

	HandleGLError(); 

	//background color
	glClearColor(1.0,1.0,1.0,0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	

	// Set the camera parameters
  	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	camera->glPlaceCamera();
	
	
	//creates light

	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

	GLfloat position[4] = { -10,-30,30, 1};
	GLfloat diffuse[4] = { 0.7,0.7,0.7,1};
	GLfloat specular[4] = { 0.2,0.2,0.2,1};
	GLfloat ambient[4] = { 0.3, 0.3, 0.3, 1.0 };

	glLightfv(GL_LIGHT0, GL_POSITION, position);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);

	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

	GLfloat spec_mat[4] = {1,1,1,1};
	float glexponent = 10;
	glMaterialfv(GL_FRONT, GL_SHININESS, &glexponent);
	glMaterialfv(GL_FRONT, GL_SPECULAR, spec_mat);

	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	float back_color[] = { 0.2,0.8,0.8,1};
	glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, back_color);
	glEnable(GL_LIGHT0);


	/*The following block commented code draws in coordinate lines that
		are 40 units long.
	*/
	//start lines code
	if(axes == true){
		float length = 50.0;
		//float eps = .03;
		glBegin(GL_LINES);
		//x line is blue (dark is positive)	
		glColor3f(0.6f, 0.8f, 1.0f);
		glVertex3f(-length, 0.0f, 0.0f);
		glColor3f(0.0f, 0.2f, 0.4f);
		glVertex3f(length, 0.0f, 0.0f);	
		
		//y line is red (dark is positive)
		glColor3f(1.0f, 0.4f, 0.8f);
		glVertex3f(0.0f, -length, 0.0f);
		glColor3f(0.4f, 0.0f, 0.0f);
		glVertex3f(0.0f, length, 0.0f);
	
		//z line is green (dark is positive)	
		glColor3f(0.0f, 0.4f, 0.2f);
		glVertex3f(0.0f, 0.0f, length);
		glColor3f(0.0f, 1.0f, 0.4f);
		glVertex3f(0.0f, 0.0f, -length);
		glEnd();
	}

//	draw();
	teatime();
	//end lines code	
	glutPostRedisplay();
	glutSwapBuffers();
}

int main(int argc, char** argv) {
	
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(600, 400);
	
	if(argc != 2){
		cout << "Please choose a transformation. " << endl;
	}
	trans = atoi(argv[1]);
	cout << "Press 0 to return to untransformed teapot" << endl
		<< "Press 1 for a traslation" << endl
		<< "Press 2 for a scaling" << endl
		<< "Press 3 for a rotation" << endl
		<< "Press 4 for a shearing" << endl
		<< "Press a to toggle axes on and off" << endl
		<< "Press m to toggle mesh teapot on and off" << endl
		<< "Press q or esc to quit" << endl;
	
	glutCreateWindow("Transformations");
	initRendering();
	
	glutDisplayFunc(drawScene);
	glutKeyboardFunc(handleKeypress);
	glutReshapeFunc(handleResize);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);

	glutMainLoop();
	return 0;
}

